(SST) ShlWAPI.pas Version 1.08

Developer Reference
(SST) ShlWAPI DllGetVersion Function
Function name and prototype/template declaration for a function that returns information on the version of a dynamic link library (dll).
Scope
Not applicable (see remarks, below).
Syntax
Function DllGetVersion(dllversioninfop : PDllVersionInfo) : HRESULT;
Parameters
dllversioninfop [in] Pointer to, or address of a record/structore of type TDllVersionInfo or TDllVersionInfo2. .
Return Values
DllGetVersion implementations should return S_OK (= 0) to indicate successful execution and a COM-defined error code*1 if they fail.
The implementation as found in ShlWAPI.dll version 6.0.6001.18000 (as is part of Windows Vista Business, with SP 1 & IE8) returns S_OK (= 0) to indicate success and, based on a superficial tests, 0x80070057 ("The parameter is incorrect") when simulating a caller's typical implementation errors, such as passing an invalid pointer/address to the function or forgetting to initialize the record/structure's cbSize field.
Remarks
Although DllGetVersion is implemented in and exported by name by many Windows, shell/common control (ComCtrl) dlls, including the ShlWAPI.dlls, it is merely a recommendation for a function that returns a dynamic link library's version.
Even if the function is starting to/has become a de facto standard *2, its implementation is by no means mandatory and its existence in a dll should not be taken for granted. It should therefore never be listed in the in the hard-coded "imports" table of a binary (PE) executable, as this will prevent the application's execution in the event that a dll, in which it is not exported, is loaded. That could happen for a number of reasons, ranging from a faulty build (of the dll) to an incomplete/incorrect installation, or as a result of the paths listed in the PATH environment variable(s).
 
(*1) The Microsoft documentation on the function refers to the error codes returned by the function in the event of failure as COM-defined error codes. Even if this is by no means incorrect, it is slightly misleading, in that it suggests that the error codes that are (or should be) returned, were generated by COM functions and/or are actually COM specific error codes. Whereas, in fact, the schema that applies to all (32-bit) Windows error codes is closely related to the Windows NT event log and message text resource APIs. All error codes are therefore assumed to have the format shown in the table below and should be defined and processed accordingly.
  High Word Low Word
  Sev. C R Facility code Error code
Bit number: 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
0x8000 0000 = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0x8007 0000 = 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Sev. Abbreviation for "Severity". Two bits (bits 31 & 30, 0-based) that provide information on the severity of the error. If both of these bits are zero, the bits in the low word represent a code that denotes success, otherwise they represent an error.
C Abbreviation for "Customer". A single bit (bit 29, 0-based) that is never set in system errors, but may be set by developers to denote that the error has a non-Windows source.
R Abbreviation for "Reserved". A single, reserved bit (bit 28, 0-based).
Facility code A 12-bit code that provides rudimentary info on the source/origin (and/or nature) of the error. Although this code may be freely defined by the author(s) of the error code, the first 256 values are reserved for use by the operating system, approx. 50, of which had been defined in Microsoft SDK version 6.1.
Of particular interest, in this context, is the symbolic name assigned to the value 7. This being FACILITY_WIN32, which typifies the value in the low word as a "common", system, error code (i.e. an error code as returned by ordinary, non-COM, Windows, API functions).
Error code A 16 bit value that is the error code. This can be freely defined by the developer(s) of the software in which the error occurred.
Much of this information has to or may optionally be specified in event log/message text definitions, thereby exemplifying the connection(s) between error codes, user friendly, runtime, error messages, the Windows event log, and message tables/resources.
MessageId=87 ;//Numerical identifier for this ;//particular error code and text Severity=0x02 ;//= Warning Facility=0x07 ;//= FACILITY_WIN32 SymbolicName=MSG_EXAMPLE_ERROR_1 ;//An arbitrary but unigue, identifier string ;//for this message definition Language=English ;//The language of the message text Function %1 failed with error code %2. ;//The English language error message text ;//with two insertion strings . ;//Terminates this message table def.
Thus, when split into its components, the error code 0x80070057 tells us that the function that returned this value was unable to initiate and/or complete the operation as intended (i.e. faultlessly) and that the error code in the low word is a system error code.
Which, brings us to the values that should be returned when implementing DllGetVersion functions and why it is necessary to keep the relationship between error codes' values and the Windows event log (and message text resources) in mind.
Developers of DllGetVersion functions are, of course, free to return any error codes they see fit, even if they are only meaningful to the manufacturer of the dll. However, such error codes can easily defeat the purpose for implementing the function, this being to provide a standardized method for retrieving version information from the dll. Furthermore, it would probably also entail the following issues/problems:
  • Prorietary error handling routines would have to be implemented in all modules that depend on the version information from the dll, possibly in addition to routines for conformant error handling.
  • Other applications and the system could misinterpret the returned error codes.
  • As error codes by themselves are rarely meaningful to users and the standard error texts could no longer simply be retrieved by means of the Windows API function FormatMessage, string and, possibly, message table resources would have to be defined and linked into one of the application's modules, introducing several new/additional problems, such as translations, versioning, installation, etc..
  • Logging such an error in the Windows event log is/would be problematic, because once the application, and with it the module in which the error texts are stored, is uninstalled, the Windows event log would no longer be able to display the error message.
(*2) Numerous dlls, including the dlls of third party manufacturers, that aren't part of the Windows shell, have implemented and export the function. .
Example How to Implement
The example below, demonstrates how to implement a DllGetVersion function. To use it, create a Delphi, Dll project and add a unit with the example code to the project. Finally, export the DllGetVersion function by adding an "Exports" clause with DllGetVersion to the dll's project source code (i.e. the .dpr file). Note, that the function has to be exported by name and not (only) by ordinal.
unit DllGetVerExample01; interface USES Windows, SyncObjs, ShlWAPI; ... const MYDLL_MAJORVERSION = 1; const MYDLL_MINORVERSION = 0; const MYDLL_BUILDNUM = 42; const MYDLL_PLATFORMIDBOTH = DLLVER_PLATFORM_WINDOWS; //= $00000001; const MYDLL_PLATFORMIDWINNT = DLLVER_PLATFORM_NT; //= $00000002; const MYDLL_QFE = 88; ... FUNCTION DllGetVersion(dllversioninfop : PDllVersionInfo) : HRESULT; STDCALL; FORWARD; FUNCTION DllRetVersionInfo(dllversioninfop : PDllVersionInfo) : HRESULT; FORWARD; ... implementation ... Function DllGetVersion(dllversioninfop : PDllVersionInfo) : HRESULT; Var retval : HRESULT; Var critsection : TCriticalSection; Begin retval := 0; //= S_OK critsection := NIL; Try critsection := TCriticalSection.Create(); Try critsection.Enter(); retval := DllRetVersionInfo(dllversioninfop); critsection.Leave(); Finally critsection.Free(); end; Except raise; //Or implement some other form of error handling end; DllGetVersion := retval; End; Function DllRetVersionInfo(dllversioninfop : PDllVersionInfo) : HRESULT; Var retval : HRESULT; begin retval := 0; //= S_OK if dllversioninfop <> NIL then begin if IsBadWritePtr(dllversioninfop, SizeOf(DWORD)) = FALSE then begin if dllversioninfop^.cbSize >= SizeOf(TDllVersionInfo) then begin if IsBadWritePtr(dllversioninfop, SizeOf(TDllVersionInfo)) = FALSE then begin dllversioninfop^.dwMajorVersion := MYDLL_MAJORVERSION; dllversioninfop^.dwMinorVersion := MYDLL_MINORVERSION; dllversioninfop^.dwBuildNumber := MYDLL_BUILDNUM; //If the dll was built for and requires Windows NT //dllversioninfop^.dwPlatformID := MYDLL_PLATFORMIDWINNT; //else //othervise dllversioninfop^.dwPlatformID := MYDLL_PLATFORMIDBOTH; end else retval := $80070057; //= $80070000 + ERROR_INVALID_PARAMETER or ERROR_NOACCESS (= 998) if (retval = S_OK) and (dllversioninfop^.cbSize = SizeOf(TDllVersionInfo2)) then begin if IsBadWritePtr(dllversioninfop, SizeOf(TDllVersionInfo2)) = FALSE then begin PDllVersionInfo2(dllversioninfop)^.dwFlags := 0; PDllVersionInfo2(dllversioninfop)^.ullVersion := Int64(MakeDllVerULL(MYDLL_MAJORVERSION, MYDLL_MINORVERSION, MYDLL_BUILDNUM, MYDLL_QFE)); end else retval := $80070057; //= $80070000 + ERROR_MORE_DATA or ERROR_NOACCESS (= 998) end end else begin dllversioninfop^.cbSize := SizeOf(TDllVersionInfo2); retval := $800700EA; //ERROR_MORE_DATA (= 234) or ERROR_INSUFFICIENT_BUFFER (= 122) end; end else retval := $80070057; //= $80070000 + ERROR_INVALID_PARAMETER; end else retval := $80070057; //= $80070000 + ERROR_INVALID_PARAMETER; DllRetVersionInfo := retval; end; end; //End of unit
Example How to Call
FUNCTION TForm4.GetDllHandle(adllanme : STRING) : HMODULE; VAR rethandle : HMODULE; BEGIN rethandle := 0; rethandle := GetModuleHandle(PChar(adllanme)); GetDllHandle := rethandle; END; FUNCTION TForm4.IsDllVerInfoImplemted(adllhandle : HMODULE; VAR aprocaddr : POINTER) : BOOLEAN; VAR retval : BOOLEAN; BEGIN retval := FALSE; IF (adllhandle <> 0) AND (adllhandle <> INVALID_HANDLE_VALUE) THEN BEGIN aprocaddr := GetProcAddress(adllhandle, 'DllGetVersion'); IF aprocaddr <> NIL THEN retval := TRUE; END; IsDllVerInfoImplemted := retval; END; FUNCTION TForm4.GetDllVersionInfoVer(aprocaddr : POINTER; VAR adllverinforec : TDllVersionInfo2) : INTEGER; //Although some DllGetVersion implementations, may return the size of //the supported record when called with the cbSize member set to 0, //others may not. It is thus safer to call the function with the //larger record first and evaluate the return value. VAR retval : INTEGER; VAR getverretval : HRESULT; VAR newinfoline : STRING; BEGIN retval := 0; getverretval := 0; newinfoline := ''; IF aprocaddr <> NIL THEN BEGIN adllverinforec.info1.cbSize := SizeOf(adllverinforec); getverretval := TDllGetVersionProc(aprocaddr)(@adllverinforec); IF getverretval = S_OK THEN retval := 2 ELSE retval := 1; END ELSE retval := - 1; GetDllVersionInfoVer := retval; END; FUNCTION TForm4.GetDllVersionInfo(aprocaddr : POINTER; VAR adllverinforec : TDllVersionInfo) : BOOLEAN; VAR retval : BOOLEAN; VAR getverretval : HRESULT; BEGIN retval := FALSE; getverretval := 0; IF aprocaddr <> NIL THEN BEGIN FillChar(adllverinforec, SizeOf(adllverinforec), #0); adllverinforec.cbSize := SizeOf(adllverinforec); getverretval := TDllGetVersionProc(aprocaddr)(@adllverinforec); IF getverretval = S_OK THEN retval := TRUE; END; GetDllVersionInfo := retval; END; FUNCTION TForm4.GetDllVersionInfo2(aprocaddr : POINTER; VAR adllverinforec : TDllVersionInfo2) : BOOLEAN; VAR retval : BOOLEAN; VAR getverretval : HRESULT; BEGIN retval := FALSE; getverretval := 0; IF aprocaddr <> NIL THEN BEGIN FillChar(adllverinforec, SizeOf(adllverinforec), #0); adllverinforec.info1.cbSize := SizeOf(adllverinforec); getverretval := TDllGetVersionProc(aprocaddr)(@adllverinforec); IF getverretval = S_OK THEN retval := TRUE; END; GetDllVersionInfo2 := retval; END; PROCEDURE TForm4.TestDllGetVersion(Sender : TObject); VAR dllhandle : HMODULE; VAR verinfosupported : BOOLEAN; VAR procaddr : POINTER; VAR dllverinfover : INTEGER; VAR dllverinfo2rec : TDllVersionInfo2; VAR getverretval : BOOLEAN; VAR ullversionval : WORD; VAR newinfoline : STRING; BEGIN dllhandle := 0; verinfosupported := FALSE; procaddr := NIL; dllverinfover := 0; FillChar(dllverinfo2rec, SizeOf(dllverinfo2rec), #0); getverretval := FALSE; newinfoline := ''; dllhandle := GetDllHandle('ShlWAPI.dll'); verinfosupported := IsDllVerInfoImplemted(dllhandle, procaddr); IF verinfosupported THEN BEGIN dllverinfover := GetDllVersionInfoVer(procaddr, dllverinfo2rec); IF dllverinfover >= 1 THEN BEGIN newinfoline := 'The loaded ShlWAPI.dll supports DllGetVersion, version ' + IntToStr(dllverinfover) + ' records'; Memo1.Lines.Add(newinfoline); IF dllverinfover = 1 THEN getverretval := GetDllVersionInfo(procaddr, dllverinfo2rec.info1) ELSE IF dllverinfover = 2 THEN getverretval := GetDllVersionInfo2(procaddr, dllverinfo2rec); IF getverretval = TRUE THEN BEGIN newinfoline := 'DllGetVersion returned the following version information: '; Memo1.Lines.Add(newinfoline); newinfoline := 'Major version : ' + IntToStr(dllverinfo2rec.info1.dwMajorVersion); Memo1.Lines.Add(newinfoline); newinfoline := 'Minor version : ' + IntToStr(dllverinfo2rec.info1.dwMinorVersion); Memo1.Lines.Add(newinfoline); newinfoline := 'Build number : ' + IntToStr(dllverinfo2rec.info1.dwBuildNumber); Memo1.Lines.Add(newinfoline); newinfoline := 'Platform ID : ' + IntToStr(dllverinfo2rec.info1.dwPlatformID); Memo1.Lines.Add(newinfoline); IF dllverinfover = 2 THEN BEGIN newinfoline := 'ullVersion : 0x' + IntToHex(dllverinfo2rec.ullVersion, 16); Memo1.Lines.Add(newinfoline); newinfoline := 'ullVersion broken down into : '; Memo1.Lines.Add(newinfoline); //Make the unwieldy term on the right a little easier to handle ullversionval := HIWORD(ULARGE_INTEGER(dllverinfo2rec.ullVersion).HighPart); newinfoline := 'Major version : ' + IntToStr(ullversionval) + ' (0x' + IntToHex(ullversionval, 4) + ')'; Memo1.Lines.Add(newinfoline); ullversionval := LOWORD(ULARGE_INTEGER(dllverinfo2rec.ullVersion).HighPart); newinfoline := 'Minor version : ' + IntToStr(ullversionval) + ' (0x' + IntToHex(ullversionval, 4) + ')'; Memo1.Lines.Add(newinfoline); ullversionval := HIWORD(ULARGE_INTEGER(dllverinfo2rec.ullVersion).LowPart); newinfoline := 'Build number : ' + IntToStr(ullversionval) + ' (0x' + IntToHex(ullversionval, 4) + ')'; Memo1.Lines.Add(newinfoline); ullversionval := LOWORD(ULARGE_INTEGER(dllverinfo2rec.ullVersion).LowPart); newinfoline := 'QFE : ' + IntToStr(ullversionval) + ' (0x' + IntToHex(ullversionval, 4) + ')'; Memo1.Lines.Add(newinfoline); END; END ELSE BEGIN newinfoline := 'Retrieving the version information failed'; Memo1.Lines.Add(newinfoline); END; END ELSE //IF dllverinfover >= 1 THEN BEGIN newinfoline := 'Determining the version of the version info record supported by DllGetVersion failed.'; Memo1.Lines.Add(newinfoline); END; END ELSE BEGIN newinfoline := 'The loaded ShlWAPI.dll does not support DllGetVersion'; Memo1.Lines.Add(newinfoline); END; Memo1.Lines.Add(''); TestExampleDllGetVersion(Sender); END;
Text:
The loaded ShlWAPI.dll supports DllGetVersion, version 2 records DllGetVersion returned the following version information: Major version : 6 Minor version : 0 Build number : 6001 Platform ID : 1 ullVersion : 0x0006000017714650 ullVersion broken down into : Major version : 6 (0x0006) Minor version : 0 (0x0000) Build number : 6001 (0x1771) QFE : 18000 (0x4650) The loaded MyExampleDll01.dll supports DllGetVersion, version 2 records DllGetVersion returned the following version information: Major version : 1 Minor version : 0 Build number : 42 Platform ID : 1 ullVersion : 0x00010000002A0058 ullVersion broken down into : Major version : 1 (0x0001) Minor version : 0 (0x0000) Build number : 42 (0x002A) QFE : 88 (0x0058)
Requirements
Unit: Not applicable.
Library: Not applicable.
Unicode: Not applicable.
Min. ShlWAPI.dll version according to MS SDK doc.: 4.71
Min. ShlWAPI.dll version based on SST research: 4.71
Min. OS version(s) according to Microsoft SDK doc.: Windows 98, Windows NT 4.0 with Internet Explorer 4.0, Windows 2000
Min. OS version(s) according to SST research.: Windows 98, Windows NT 4.0 with shell/IE version 4.0
See Also
TDllVersionInfo, TDllVersionInfo2, MakeDllVerULL, TDllGetVersionProc.
 
Windows APIs: DLLVERSIONINFO, DLLVERSIONINFO2, DllGetVersion, GetLastError, SetLastError, SetLastErrorEx.


Document/Contents version 1.03
Page/URI last updated on 07.12.2023
 
Copyright © Stoelzel Software Technologie (SST) 2010 - 2022
Suggestions and comments mail to:
webmaster@stoelzelsoftwaretech.com